﻿--[[
   @brief    base utility functions
   @author   sogoodchen, hqzheng
   @version  1.0
   @date     2016-01-08
--]]

base_utility = 
{    
    init = function()
        -- comm_center.reg_service('i_service_webutl_get_tgp_version_str', base_utility.service_get_tgp_version_str); 初始化太早了,comm_center还没有起来，需要在desc文件里面reg_service
    end;

    -- brief: 输出日志到文件（daemon_x.log）
    -- param: msg 消息字符串
    -- param: core_msg 是否为关键日志（发布版本中也输出），bool类型，不填写时为普通日志
    log = function(msg, core_msg)
        ierd_tgp.tpf_ui.Log(msg, core_msg);
    end;

    -- brief: 输出日志到文件（tgp_daemon_log.tlg）
    -- param: filter 过滤字段
    -- param: msg 消息字符串
    -- param: core_msg 是否为关键日志（发布版本中也输出），bool类型，不填写时为普通日志
    log_ex = function(filter, msg, core_msg)
        ierd_tgp.tpf_ui.LogEx(filter, msg, core_msg);
    end;

    -- brief: table对象深度拷贝
    -- param: src_tbl 被拷贝的table对象
    -- return: 目标table的拷贝
    copy_table = function(src_tbl)
        if(type(src_tbl) ~= 'table') then
            return src_tbl;
        end;
        
        local ret_tbl = {}
        for key, value in pairs(src_tbl) do
            if type(value) ~= 'table' then
                ret_tbl[key] = value;
            else
                ret_tbl[key] = base_utility.copy_table(value);
            end
        end
        
        return ret_tbl;
    end;

    copy_table_without_function = function(src_tbl)
        if(type(src_tbl) ~= 'table') then
            return src_tbl;
        end;

        local ret_tbl = {}
        for key, value in pairs(src_tbl) do
            if type(value) == 'table' then
                ret_tbl[key] = base_utility.copy_table_without_function(value);
            elseif type(value) ~= 'function' then
                ret_tbl[key] = value;
            end
        end

        return ret_tbl;
    end;
    
    -- brief: 把json字符串转为table对象
    -- param: json_str json字符串
    -- return: table对象， 转换失败将返回nil
    json_to_table = function(json_str)
        if json_str == nil or json_str == "" then
            return
        end
        local cjson_safe = require("cjson.safe");
        local tbl, err = cjson_safe.decode(json_str);
        if err ~= nil then
            -- base_utility.log("[utility] json_to_table occur err: ".. err);
            base_utility.inner_log("json_to_table occur err: ".. err);
        end;
        return tbl;
    end;
    
    -- brief: 把table对象转为json字符串，table对象不能有函数、usertype等复杂数据类型的成员
    -- param: tbl table对象
    -- return: json字符串， 转换失败将返回nil
    table_to_json = function(tbl)
        local cjson_safe = require("cjson.safe");
        local json_str, err = cjson_safe.encode(tbl);
        if err ~= nil then
            -- base_utility.log("[utility] table_to_json occur err: ".. err);
            base_utility.inner_log("table_to_json occur err: ".. err);
        end;
        return json_str;
    end;

    load_ini = function(fileName)
        assert(type(fileName) == 'string', 'Parameter "fileName" must be a string.');
        local fileObj = ierd_tgp.common.LuaFileObject:new();
        if nil == fileObj then
            base_utility.inner_log("new file object error, load ini failed");
            return nil;
        end
        local data = {};
        local section;
        if fileObj:open(fileName, 'r') then
            local str = NewStr(512);
            local line = fileObj:get_line(str, 512);
            while nil ~= line do
                line = line:gsub("^%s*(.-)%s*$", "%1");--trim
                local tempSection = line:match('^%[([^%[%]]+)%]$');
                if(tempSection)then
                    section = tonumber(tempSection) and tonumber(tempSection) or tempSection;
                    data[section] = data[section] or {};
                end
                local param, value = line:match('^([%w|_]+)%s-=%s-(.+)$');
                if(param and value ~= nil)then
                    if(tonumber(value))then
                        value = tonumber(value);
                    elseif(value == 'true')then
                        value = true;
                    elseif(value == 'false')then
                        value = false;
                    end
                    if(tonumber(param))then
                        param = tonumber(param);
                    end
                    if data[section] ~= nil then
                        data[section][param] = value;
                    end
                end
                line = fileObj:get_line(str, 512);
            end
        end
        fileObj:delete();
        return data;
    end;

    save_ini = function(fileName, data)
        assert(type(fileName) == 'string', 'Parameter "fileName" must be a string.');
        assert(type(data) == 'table', 'Parameter "data" must be a table.');
        local fileObj = ierd_tgp.common.LuaFileObject:new();
        if nil == fileObj then
            base_utility.inner_log("new file object error, save ini failed");
            return false;
        end
        if fileObj:open(fileName, "w+b") then
            local contents = '';
            for section, param in pairs(data) do
                contents = contents .. ('[%s]\n'):format(section);
                for key, value in pairs(param) do
                    contents = contents .. ('%s=%s\n'):format(key, tostring(value));
                end
                contents = contents .. '\n';
            end
            fileObj:write(contents, contents:len());
            fileObj:close();
        end
        fileObj:delete();
        return true;
    end;

    -- 检查文件是否存在
    -- file_path：文件全路径 utf8编码
    -- return: 文件存在返回true，否则返回false
    is_file_exist = function(file_path_u8)
        local fileObj = ierd_tgp.common.LuaFileObject:new();
        if nil == fileObj then
            base_utility.inner_log("create lua file object failed");
            return false;
        end
        local exists = false;
        if fileObj:open(file_path_u8, "r") then
            fileObj:close();
            exists = true;
        end
        fileObj:delete();
        return exists;
    end;
    
    get_file_size = function(file_path_u8)
        return ierd_tgp.common.LuaGetFileSize(file_path_u8);
    end;

    -- 格式化磁盘空间大小
    -- disk_bytes：磁盘空间大小Bytes
    -- accuracy：小数位数
    -- unit: 单位
    -- return: {valid=结果有效,fmt_value=格式化数值(number),fmt_unit=格式化单位(string)}
    format_disk_space = function(disk_bytes, unit)
        local unit_arr = {"B", "KB", "MB", "GB", "TB", "PB"};
        local unit_arrn = #unit_arr;
        local ret = {valid=false, fmt_value=0, fmt_unit=""};        
        if unit == nil then
            -- 自动设置单位
            for idx = 1, unit_arrn do
                if disk_bytes < 1000 then 
                    ret.valid = true;
                    ret.fmt_value = disk_bytes;
                    ret.fmt_unit = unit_arr[idx];
                    break;
                end
                disk_bytes = disk_bytes / 1024;
            end
        else 
            -- 指定单位
            for idx = 1, unit_arrn do
                if unit == unit_arr[idx] then 
                    ret.valid = true;
                    ret.fmt_value = disk_bytes;
                    ret.fmt_unit = unit_arr[idx];
                    break;
                end
                disk_bytes = disk_bytes / 1024;
            end
        end
        return ret;   
    end;

    fix_disk_size_unit = function(old_unit)
        if old_unit == nil then
            return "";
        end
        local size_unit = old_unit;
        if size_unit ~= "" and size_unit ~= "B" then
            size_unit =  size_unit .. "B";
        end
        return size_unit;
    end;

    roundv = function(num)
        num = tonumber(num) or 0;
        if num >= 0 then
            return math.floor(num + 0.5);
        else 
            return math.ceil(num - 0.5);
        end
    end;

    -- 与web下载管理器格式化方式一致
    format_size = function(KiBytes)
        if nil == KiBytes then
            return "-";
        end
        local num = tonumber(KiBytes);
        local KB = 1024;
        local MB = 1024 * KB;
        local GB = 1024 * MB;
        local size = '';
        if num > 1 * MB then
            size = string.format("%.1f", base_utility.roundv(num / (1 * MB) * 10) / 10) .. 'GB';
        elseif num > 1 * KB then
            size = string.format("%.1f", base_utility.roundv(num / KB * 10) / 10) .. 'MB';
        else
            size = tostring(num) .. 'KB';
        end
        return size;
    end;

    -- curl异步网络请求
    -- url: 请求网址
    -- call_back: 回调函数名，回调函数只有一个字符串参数，如果请求失败会是空字符串
    -- is_get: 是否是get请求，默认是，如果不填，忽略key_value_table
    -- key_value_table: post请求下的键值对
    -- is_auth: 是否支持在cookie中加入tgp票据和pkey以及skey
    -- user_data: 透传参数字符串
    -- json_data: 添加json数据
    -- cookie_data: 自定义需要添加的cookie数据
    -- is_high_priority: 是否为高优先级请求
    -- return: bool 是否成功执行(不是请求成功)
    curl_request_async = function(url, call_back, is_get, key_value_table, is_auth, user_data,
                                  json_data, cookie_data, is_high_priority)
        local kv_map = ierd_tgp.curl_adapter.KVStringMap:new_local();
        if not is_get and key_value_table then
            -- post 处理lua的table为c++的std::map
            for key, value in pairs(key_value_table) do
                kv_map:Add(tostring(key), tostring(value));
            end;
        end;
        if user_data == nil then
            user_data = ""
        end
    
        local custom_cookie_map = ierd_tgp.curl_adapter.KVStringMap:new_local();
        if cookie_data then
            for key, value in pairs(cookie_data) do
                custom_cookie_map:Add(tostring(key), tostring(value));
            end;
        end;

        if is_high_priority then
            is_high_priority = true;
        else
            is_high_priority = false;
        end;

        return ierd_tgp.curl_adapter.AddCurlTaskEx(url, call_back, is_get,
          kv_map, user_data, json_data, is_auth, custom_cookie_map, is_high_priority);
    end;
    
    -- curl异步网络请求上传json数据
    -- url: 请求网址
    -- call_back: 回调函数名，回调函数只有一个字符串参数，如果请求失败会是空字符串
    -- json_str: 要上传的json数据
    -- return: bool 是否成功执行(不是请求成功)
    curl_post_json_async = function(url, call_back, json_str)
        base_utility.inner_log(
            string.format("[curl_post_json_async]url:%s, call_back:%s, json_str:%s",
            url, call_back, json_str));        
        return ierd_tgp.curl_adapter.AddCurlPostJsonTask(url, call_back, json_str);
    end;

    -- 将从cgi获取的cfg返回值解析成table值
    -- cfg_rsp: cfg返回的数据，可以为空
    -- call_back_str: 请求前填的call_back字段，用于校验
    -- return: 
    --     1.bool 是否成功获取到有效的cfg,第一个返回值为false时后面返回值都为nil
    --     2.table 由cfg_name和cfg_value组成的数组
    --     3.int crc值
    cfg_string_to_table = function(cfg_rsp, call_back_str)
        -- 返回的数据格式为call_back_str(有用的数据)
        if cfg_rsp == nil or call_back_str == nil then
            return false;
        end;

        local prefix = call_back_str .. "(";
        local tail = ")";
        local outer_len = string.len(prefix .. tail);
        local rsp_len = string.len(cfg_rsp);
        if rsp_len < outer_len then
            return false;
        end;

        local prefix_start, prefix_end = string.find(cfg_rsp, prefix, 1, true); -- just find plain text
        if prefix_start == 1 then
            local tail_reg = "%" .. tail .. "[^%" .. tail .. "]*$";
            local tail_start, tail_end = string.find(cfg_rsp, tail_reg); -- find the last tail in cfg_rsp
            if tail_end == rsp_len then
                -- 说明格式正确
                local real_rsp = string.sub(cfg_rsp, prefix_end + 1, tail_start - 1);
                local real_table = base_utility.json_to_table(real_rsp);
                if real_table ~= nil and real_table["result"] == 0 then
                    if nil ~= real_table["cfg_list"] then
                        return true, real_table["cfg_list"], real_table["crc"];
                    elseif nil ~= real_table["sub_item_list"] then
                        return true, real_table["sub_item_list"], real_table["crc"];
                    end
                end
            end;
        end;

        return false;
    end;

    -- lua下递归删除目录异常的艰难,这里用C++实现一个,异步的
    -- dir_path: 需要删除的目录路径
    -- cb_func: 回调函数 回调内容为 {"error_code":int, "error_msg":string }
    remove_dir = function(dir_path, cb_func)
        ierd_tgp.common.RemoveDir(dir_path, cb_func);
    end;

    -- 转义字符串里面所有的pattern相关字符，以方便做其他字符的纯文本操作
    -- Inhibit Regular Expression magic characters ^$()%.[]*+-?)
    escape_pattern = function(str)
        -- Prefix every non-alphanumeric character (%W) with a % escape character, 
        -- where %% is the % escape, and %1 is original character
        return string.gsub(str, "(%W)","%%%1");
    end;

    -- 等于string.gsub的功能，但是会转义，进行纯粹字符匹配而不进行类似正则的匹配规则
    replace_plain = function(origin, find, replace)
        local e_find = base_utility.escape_pattern(find);
        local e_replace = base_utility.escape_pattern(replace);
        return string.gsub(origin, e_find, e_replace);
    end;

    -- 复制源目录所有文件到目标目录，如果失败，则回退所有操作，默认会覆盖已有文件
    -- 异步操作,lua本身的文件操作太弱所以用C++来实现了
    -- src_dir: 源目录
    -- dst_dir: 目标目录
    -- delete: ierd_tgp.common.LuaHelper_MovDirDelFlag.ONLY_SUCCESS 只成功后删除源目录
    --         ierd_tgp.common.LuaHelper_MovDirDelFlag.ONLY_FAILED  只失败后删除源目录
    --         ierd_tgp.common.LuaHelper_MovDirDelFlag.ALL          任何情况下操作完成后都删除源目录
    -- cb_func: 回调函数 回调内容为 {"error_code":int, "error_msg":string }
    move_dir_to_dir = function(src_dir, dst_dir, delete, cb_func)
        ierd_tgp.common.MovDirToDir(src_dir, dst_dir, delete, cb_func);
    end;

    -- 异步获取一个文件的md5
    -- file_path: 文件路径
    -- call_back_func: 回调函数
    -- call_back: {"error_code":int, "md5":string}
    md5_async = function(file_path, call_back_func)
        ierd_tgp.common.GetMD5(file_path, call_back_func);
    end;

    -- 比较版本号
    -- left: 版本号1
    -- right: 版本号2
    -- return: int 1代表版本号1更大，2代表版本号2更大 0代表一样大
    compare_version = function(left, right)
        if left == nil and right == nil then
            return 0;
        end;

        if left == nil then
            return 2;
        end;

        if right == nil then
            return 1;
        end;

        -- 上面有的utility.split有bug
        local max_index = 1;
        local index = 1
        local left_versions = {};
        local right_versions = {};
        for word in string.gmatch(left, '([^%.]+)') do
            left_versions[index] = word;
            index = index + 1;
        end

        max_index = index;
        index = 1;

        for word in string.gmatch(right, '([^%.]+)') do
            right_versions[index] = word;
            index = index + 1;
        end

        if index > max_index then
            max_index = index;
        end;

        for i = 1, max_index do
            local sub_version_left = tonumber(left_versions[i]);
            local sub_version_right = tonumber(right_versions[i]);
            if sub_version_left == nil and sub_version_right == nil then
                return 0;
            end;

            if sub_version_left > sub_version_right then
                return 1;
            end;

            if sub_version_left < sub_version_right then
                return 2;
            end;
        end; 
    end;

    -- qos_param 为LuaQosData类型
    lua_qos = function(qos_param)
        local tpfui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        tpfui:tqos_from_lua(qos_param);
        qos_param=nil;
    end;

    -- 新的QOS上报  zParam 上报数据，类型为 LuaQosDataEx
    lua_qos_ex = function(zParam)
        local tpfui = base_utility.get_component_adapter("ITpf_ui",
                            "ierd_tgp.tpf_ui.GetTPFUI();");
        tpfui:tqos_report_ex(zParam);
        zParam = nil;
    end;

    -- 新的QOS上报-离线上报  zParam 上报数据，类型为 LuaQosDataEx
    lua_qos_offline_report = function(zParam)
        local tpfui = base_utility.get_component_adapter("ITpf_ui",
                            "ierd_tgp.tpf_ui.GetTPFUI();");
        tpfui:tqos_offline_report(zParam);
        zParam = nil;
    end;

    -- qos_param 为LuaQosData类型的jsonstr
    lua_qos_str = function(qos_param)
        local info = base_utility.json_to_table(qos_param);
        if info == nil then
            return;
        end

        local qosparam = ierd_tgp.tpf_ui.LuaQosData:new();
        qosparam.oc_type = info.oc_type;
        qosparam.qos_report_id = info.qos_report_id;
        qosparam.int_count = info.int_count;
        qosparam.str_count = info.str_count;
        for i,val in ipairs(info.int_val) do
            qosparam.int_val[i-1] = val;
        end
        for i=1,info.str_count do
            qosparam["str_val" .. i] = info["str_val" .. i];
        end
        base_utility.lua_qos(qosparam);
        qosparam:delete();
    end;

    -- brief: 获取TGP当前工作目录全路径
    -- return: 全路径： utf8格式
    get_work_path = function()
        local path = ierd_tgp.tpf_ui.GetWorkPath();
        return path;
    end;

    -- 是否支持小号启动
    support_mul_instances = function(game_id)
        --[[
        return game_id == GAME_ID.GAME_ID_DNF or
            game_id == GAME_ID.GAME_ID_XGAME or
            game_id == GAME_ID.GAME_ID_SGSJ or
            game_id == GAME_ID.GAME_ID_QQX5;
        ]]
        local param = {};
        local result = {};
        param.game_id = game_id;
        param.count = 0;
        result = comm_center.call_service("Svr_GameMultiLaunchCfg",param);
        if result ~= nil and result.count ~= nil and  result.count > 1 then
            return true;
        else
            return false;
        end;
    end;

    -- brief: 通过指定符号分割字符串
    -- param: full_string:待分割的字符串; separator:分隔符
    -- return: split_array:分割后的数组
    split = function(full_string, separator)  
        local start_index = 1;  
        local split_index = 1; 
        local split_array = {};  
        while true do  
            local last_index = string.find(full_string, separator, start_index);  
            if not last_index then  
                split_array[split_index] = string.sub(full_string, start_index, string.len(full_string));  
                break;  
            end;  
            split_array[split_index] = string.sub(full_string, start_index, last_index - 1); 
            start_index = last_index + string.len(separator);  
            split_index = split_index + 1;  
        end;  
        return split_array;  
    end;  

    -- 计算字符串宽度（真实的字符个数）
    cal_str_width = function (inputstr)
        local len_in_byte = #inputstr
        local width = 0
        local i = 1
        while (i<=len_in_byte) 
        do
            local cur_byte = string.byte(inputstr, i)
            local byte_count = 1;
            if cur_byte > 0 and cur_byte <= 127 then
                byte_count = 1                                               --1字节字符
            elseif cur_byte >= 192 and cur_byte < 223 then
                byte_count = 2                                               --双字节字符
            elseif cur_byte >= 224 and cur_byte < 239 then
                byte_count = 3                                               --汉字
            elseif cur_byte >= 240 and cur_byte <= 247 then
                byte_count = 4                                               --4字节字符
            end
         
            i = i + byte_count                                              -- 重置下一字节的索引
            width = width + 1                                               -- 字符的个数（长度）
        end
        return width
    end;
    
    -- 计算限制字符长度下的字符个数
    cal_str_num_in_len = function(inputstr, max_len)
        local len_in_byte = #inputstr;
        local width = 0;
        local i = 1;
        while ( i <= len_in_byte and i <= max_len)  do
            local cur_byte = string.byte(inputstr, i)
            local byte_count = 1;
            if cur_byte > 0 and cur_byte <= 127 then
                byte_count = 1                                               --1字节字符
            elseif cur_byte >= 192 and cur_byte < 223 then
                byte_count = 2                                               --双字节字符
            elseif cur_byte >= 224 and cur_byte < 239 then
                byte_count = 3                                               --汉字
            elseif cur_byte >= 240 and cur_byte <= 247 then
                byte_count = 4                                               --4字节字符
            end
         
            i = i + byte_count                                              -- 重置下一字节的索引
            width = width + 1                                               -- 字符的个数（长度）
        end;
        return width;
    end;

    -- 内部使用的帮助函数，用于log增加本模块的filter
    inner_log = function (msg, core_msg)
        base_utility.log(string.format("[base_utility] %s", msg), core_msg);
    end;
    -- 判断一个表是否是空表
    is_empty_table = function(tbl)
        return _G.next(tbl) == nil;
    end;

    -- 去除字符串首尾的空格
    trim = function(s)
        return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
    end;

    -- 数字转带逗号的字符串例如12345->12,345
    -- 非数字的会返回nil
    convert_number_to_string = function(number)
        if type(number) == "number" then
            local s = tostring(number);
            local index = string.find(s, "%.");
            local d = "";
            if index ~= nil then
                d = string.sub(s, index, -1);  -- 小数部分
                s = string.sub(s, 1, index - 1);  -- 整数部分
            end;
            
            s = s:reverse();
            s = string.gsub(s, "%d%d%d", "%1,");
            s = string.gsub(s, "%,%-$", "%-");  -- 去掉可能多余的一个逗号
            s = string.gsub(s, "%,$", "");
            s = s:reverse();
            s = s .. d;
            return s;
        end;
        
        return nil;
    end;

    -- 将从oss系统获取的返回值解析成table值
    -- oss_rsp: oss返回的数据，可以为空
    -- var_name: 返回的js变量名字段，用于校验
    -- return: 
    --     1.bool 是否成功获取到有效的结果,第一个返回值为false时后面返回值都为nil
    --     2.table 由js变量具体json字符串转换成的table
    oss_string_to_table = function(oss_rsp, var_name)
        if nil == oss_rsp then
            return false;
        end;

        if nil == var_name then
            return false;
        end;

        local prefix = "var "..var_name.." = ";
        local tail = ";";
        local outer_len = string.len(prefix .. tail);
        local rsp_len = string.len(oss_rsp);
        if rsp_len < outer_len then
            return false;
        end;

        local prefix_start, prefix_end = string.find(oss_rsp, prefix, 1, true);
        if prefix_start == 1 then
            local tail_reg = "%" .. tail .. "[^%" .. tail .. "]*$";
            local tail_start, tail_end = string.find(oss_rsp, tail_reg);
            if tail_end == rsp_len then
                -- 说明格式正确
                local real_rsp = string.sub(oss_rsp, prefix_end + 1, tail_start - 1);
                local real_table = base_utility.json_to_table(real_rsp);
                if real_table ~= nil and real_table["data"] ~= nil then
                    return true, real_table;
                end;
            end;
        end;
        return false;
    end;

    get_component_adapter = function (component_name, func_name)
        -- load component
        local is_load_ibery = LoadComponent(component_name);
        if is_load_ibery then
            -- 执行func
            local func_script = "g_get_component_adapter_ret = " .. func_name;
            local func = loadstring(func_script);
            func();
            return g_get_component_adapter_ret;
        else
            base_utility.log(string.format(
                "[get_component_adapter]load component(%s)fail!", component_name));
            return nil;
        end
    end;

    add_interval = function (callback_func, time, user_data)
        local tpfui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        if nil ~= tpfui then
            tpfui:AddInterval(callback_func, time, user_data);
        end
    end;

    remove_interval = function (callback_func, user_data)
        local tpfui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        if nil ~= tpfui then
            tpfui:RemoveInterval(callback_func, user_data);
        end
    end;

    -- 精准广告数据上报
    accurate_ad_report = function (int_data_list, int_data_count, str_data_list, str_data_count)
        if nil == int_data_list or nil == str_data_list then
            return;
        end
        base_utility.log(string.format("int_data_count:%s, str_data_count:%s",
            tostring(int_data_count), tostring(str_data_count)));
        local game_lib =
            base_utility.get_component_adapter("IGame_library",
                "ierd_tgp.game_library.Get_game_library_adapter();");
        if nil ~= game_lib then
            local report_info = ierd_tgp.game_library.AccurateADData:new();
            report_info.int_count = int_data_count;
            for int_index = 0, int_data_count - 1 do
                report_info.int_data_list[int_index] = int_data_list[int_index + 1];
            end

            report_info.str_count = str_data_count;

            for str_index = 0, str_data_count - 1 do
                report_info.str_data_list[str_index] = ierd_tgp.game_library.AccurateADStrData:new();
                report_info.str_data_list[str_index].str_data = str_data_list[str_index + 1];
            end
            
            game_lib:AccurateADReport(report_info);
            report_info:delete();
        end
    end;
    
    get_tgp_id = function()
        local user_game_info = ierd_tgp.users_info.User_game_info:new_local();
        local users_info_adapter =
            base_utility.get_component_adapter("IUsers_info_module",
                "ierd_tgp.users_info.GetUsersInfoModuleAdapter();");
        if nil ~= users_info_adapter then
            users_info_adapter:get_self_game_info(user_game_info);
            return user_game_info.ullTGPID;
        end
        return nil;
    end;

    -- 通过cgi接口获取自定义计数
    get_accumulate_count = function (item, subitem_list, uin, callback, is_high_priority, userdata)
        local url = string.format(LoadStr("GET_ACCUMULATE_COUNT"),
            item, subitem_list, uin);
        --url = web_utility.make_login_status_url(url, uin);
        base_utility.curl_request_async(url, callback, true, nil, true, userdata,
            nil, nil, is_high_priority);
    end;

    -- 通过cgi接口设置自定义计数
    set_accumulate_count = function (item, sub_item, add_num, uin ,callback, user_data)
        local url = string.format(LoadStr("SET_ACCUMULATE_COUNT"), 
            item, sub_item, add_num, uin);
        --url = web_utility.make_login_status_url(url, uin);
        base_utility.curl_request_async(url, callback, true, nil, true, user_data);
    end;

    -- 主框架数据上报
    mainframe_report = function (action, sub_action, item_type, game_id)
        base_utility.log(
            string.format("[mainframe_report]action:%s, sub_action:%s",
                    tostring(action),
                    tostring(sub_action)));
        local report_data = ierd_tgp.tpf_ui.LuaQosDataEx:new();
        local pv_data = {};
        if nil ~= item_type then
            pv_data["type"] = item_type;
            base_utility.log(string.format("[mainframe_report]item_type:%s",
                    tostring(item_type)));
            if nil ~= game_id then
                pv_data["game_id"] = game_id;
            end
        end

        if nil ~= report_data then
            report_data.oc_type = 1;
            report_data.qos_kind = ierd_tgp.adapt_for_imports.qos.Qos_kind.kGameUpdateLackSpace;
            report_data.str_action = action;
            report_data.str_type = sub_action;
            report_data.str_json = base_utility.table_to_json(pv_data);
            base_utility.lua_qos_ex(report_data);
            report_data:delete();
        end
    end;

    service_get_tgp_version_str = function(data_in)
        -- tgp version
        local tgp_ver_u8 = NewStr(260);
        local gamelib =
            base_utility.get_component_adapter("IGame_library",
                "ierd_tgp.game_library.Get_game_library_adapter();");
        local dev_version = false;
        dev_version = gamelib:get_tgp_version(tgp_ver_u8, 260, dev_version);
        local tgp_ver = tostring(tgp_ver_u8);

        local data_out = {version=tgp_ver};
        return data_out;
    end;

    -- 判断游戏是否是由launcher启动的
    is_type_of_launcher = function()
        local launcher_info = comm_center.call_service("Srv_IsLaunchedByGame", {});
        base_utility.log("get is_launcher_info , = " .. base_utility.table_to_json(launcher_info));
        if launcher_info ~= nil and launcher_info.is_launcher ~= nil then
            if launcher_info.is_launcher == 1 then
                return true;
            else
                return false;
            end;
        else
            return false;
        end;
    end;

    -- 获得launcher启动的游戏携带的参数;如果不是launcher启动的游戏，则返回{}
    get_launcher_params = function()
        local params = {};
        local is_launcher = base_utility.is_type_of_launcher();
        if is_launcher == true then
            local launcher_data = comm_center.call_service("Srv_GetLauncherInfo", {});
            if launcher_data ~= nil and launcher_data.info ~= nil and launcher_data.info ~= "" then
                params = base_utility.json_to_table(launcher_data.info);
            end;
        end;
        return params;
    end;

    -- 是否是国际化版本
    is_type_of_oversea_ver = function()
        local ret_tbl = comm_center.call_service("Svr_IsI18NVer", {});
        if nil ~= ret_tbl and (1 == ret_tbl["is_overseas_ver"]) then
            return true;
        else
            return false;
        end;
    end;

    get_current_language = function()
        local tpf_ui =  base_utility.get_component_adapter("ITpf_ui", "ierd_tgp.tpf_ui.GetTPFUI();");
        local language = tpf_ui:get_current_language();
        return language;
    end;

    get_currrent_language_str = function()
        local language_str = "zh_CN";
        local language = base_utility.get_current_language();
        if language == 0 then
            language_str = "zh_CN";
        elseif language == 1 then
            language_str = "zh_HK";
        elseif language == 2 then
            language_str = "en_US";
        end;
        return language_str;
    end;
  
    -- 由域名字符串ID得到完整域名, 返回UTF-8的字符串
    load_domain_str = function(strID)
        local tpf_ui = base_utility.get_component_adapter("ITpf_ui", "ierd_tgp.tpf_ui.GetTPFUI();");
        local str = "";
        if (tpf_ui ~= nil) and (strID ~= nil) then
            str = tpf_ui:LoadDomainStr(U8toCUIStr(strID));
        end;
        return str;
    end;

    merge_table = function (t1, t2)
        for k,v in pairs(t2) do
            if type(v) == "table" then
                if type(t1[k] or false) == "table" then
                    base_utility.merge_table(t1[k] or {}, t2[k] or {})
                else
                    t1[k] = v
                end
            else
                t1[k] = v
            end
        end
        return t1
    end;

    table_is_empty = function (tb)
        return _G.next(tb) == nil
    end;

    get_client_version_type = function ()
        local client_type = base_utility.CLIENT_TYPE.CHINA_VER;
        local tpf_ui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        if nil ~= tpf_ui then
            client_type = tpf_ui:get_client_version_type();
        end

        return client_type;
    end;

    on_send_broadcast = function(info)
        if info == nil or info.command == nil or info.data == nil then
            base_utility.log("[E][base_utility][on_send_broadcast] data invalid", true);
            return;
        end
        local msg = base_utility.json_to_table(info.data);
        comm_center.send_broadcast(info.command, msg);
    end;

    on_call_service = function(info)
        if info == nil or info.command == nil or info.data == nil then
            base_utility.log("[E][base_utility][on_call_service] data invalid", true);
            return;
        end
        local msg = base_utility.json_to_table(info.data);
        comm_center.call_service(info.command, msg);
    end;
    
    is_develop_client = function()
        local is_dev = false;
        local game_lib = base_utility.get_component_adapter("IGame_library",
                "ierd_tgp.game_library.Get_game_library_adapter();");
        if game_lib ~= nil then
            is_dev = game_lib:Is_Develop_ver();
        end;
        return is_dev;
    end;

    is_netbar_ver = function ()
        local is_netbar = false;
        local game_lib =
        base_utility.get_component_adapter("IGame_library",
            "ierd_tgp.game_library.Get_game_library_adapter();");
        if nil ~= game_lib then
            local channel_id = game_lib:get_bind_channel_id();
            if 1 == channel_id then
                is_netbar = true;
            end
        end

        return is_netbar;
    end;

    get_debug_value = function (section, key, defaultVal, filename)
        filename = filename or "c:\\wgdebug.txt";
        if not base_utility.is_file_exist(filename) then
            return defaultVal;
        end;
        data = base_utility.load_ini(filename);
        if data == nil or data[section] == nil or data[section][key] == nil then
            return defaultVal;
        end
        return data[section][key];
    end;

    get_client_id = function ()
        local client_id = 0;
        local tpf_ui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        if nil ~= tpf_ui then
            client_id = tpf_ui:get_client_id();
        end

        return client_id;
    end;

    is_develop_client = function()
        local is_dev = false;
        local game_lib = base_utility.get_component_adapter("IGame_library",
                "ierd_tgp.game_library.Get_game_library_adapter();");
        if game_lib ~= nil then
            is_dev = game_lib:Is_Develop_ver();
        end;
        return is_dev;
    end;

    download_files = function (url)
        local data = {};
        data.url = url;
        local rsp = comm_center.call_service("i_service_download_tpfui_file", data);
        base_utility.log(
            string.format("[base_utility][download_files]url:%s, task_id:%d, bfinished:%s",
                data.url, rsp.task_id, tostring(rsp.is_finished)));
    end;

    string_begin_with = function (str, start_str)
        return string.sub(str, 1, string.len(start_str)) == start_str;
    end;

    string_end_with = function (str, end_str)
       return end_str =='' or string.sub(str, -string.len(end_str)) == end_str;
    end;

    is_big_picture_ui = function()
        local ui_info = comm_center.call_service("i_service_get_ui_mode", {});
        if ui_info ~= nil and ui_info.bigpic_ui == 1 then
            return true;
        end
        return false;
    end;

    get_color_mode = function()
        local color_mode = 0;
        local rsp = comm_center.call_service("SvrTGPConfigMgr_GetColorMode", {});
        if rsp ~= nill then
            color_mode = rsp.color_mode;
        end
        return color_mode;
    end;

    CLIENT_TYPE = {
        CHINA_VER = 1,    -- 国内版本
        OVERSEA_VER = 2   -- 海外版
    };

    LANG_TYPE = {
        ZH_CN = 0,  -- 简体中文
        ZH_HK = 1,  -- 繁体中文
        EN_US = 2,  -- 英文
    };

    CLIENT_UI_MODE = {
        NORMAL_WINDOW_UI = 1,     -- 普通模式
        BIG_PICTURE_UI = 2        -- 大屏模式
    }
};

base_utility.init();